﻿using System;
using System.IO;
using BITUI.Audio;
using SharpAudio.Codec.Wave;

namespace SharpAudio.Codec.Wave
{
    internal abstract class WavParser
    {
        public abstract int BitsPerSample { get; }

        public abstract byte[] Parse(BinaryReader reader, int size, WaveFormat format);

        public static WavParser GetParser(WaveFormatType type)
        {
            switch (type)
            {
                case WaveFormatType.Pcm: return new PcmParser();
                case WaveFormatType.DviAdpcm: return new DviAdpcmParser();
                default: throw new NotSupportedException("Invalid or unknown .wav compression format!");
            }
        }
    }

    public class AudioData_Wav : IAudioData
    {
        private RiffHeader _header;
        private WaveFormat _format;
        private WaveFact _fact;
        private WaveData _data;
        private byte[] _decodedData;




        public int BitsPerSample
        {
            get;
            private set;
        }
        public int BytePerSample
        {
            get
            {
                return BitsPerSample / 8;
            }
        }

        public string[] ExtNames => new string[] { ".wav" };
        public int Freq
        {
            get;
            private set;
        }

        public int Channel
        {
            get;
            private set;
        }

        public float Time
        {
            get;
            private set;
        }

        int seek = 0;
        public float ReadTime
        {
            get
            {
                return (float)((float)seek / _decodedData.Length) * Time;
            }

        }
        public void Reset()
        {
            seek = 0;
        }
        public void SeekTo(float _time)
        {
            seek = (int)(_time / Time * _decodedData.Length);
            seek /= (BytePerSample * Channel);
            seek *= (BytePerSample * Channel);
            if (seek < 0)
                seek = 0;
            if (seek > _decodedData.Length)
                seek = _decodedData.Length;
        }


        public void Dispose()
        {

        }


        public bool Load(string filename)
        {
            var ms = new MemoryStream();
            return Load(ms);
        }

        public bool Load(Stream stream)
        {
            using (BinaryReader br = new BinaryReader(stream))
            {
                _header = RiffHeader.Parse(br);
                _format = WaveFormat.Parse(br);

                if (_format.AudioFormat != WaveFormatType.Pcm)
                {
                    _fact = WaveFact.Parse(br);
                }

                _data = WaveData.Parse(br);
                var variant = WavParser.GetParser(_format.AudioFormat);

                _decodedData = variant.Parse(br, (int)_data.SubChunkSize, _format);

                BitsPerSample = variant.BitsPerSample;
                Channel = _format.NumChannels;
                Freq = (int)_format.SampleRate;

                Time = (float)_decodedData.Length / ((BitsPerSample / 8) * Channel * Freq);

            }
            stream.Dispose();
            return true;
        }

        float decodeOne()
        {
            float s = 0;

            if (BytePerSample == 1)
            {
                s = _decodedData[seek] / 255.0f;
                seek++;
            }
            else if (BytePerSample == 2)
            {
                var sv = BitConverter.ToInt16(_decodedData, seek);
                s = (float)sv / short.MaxValue;
                seek += 2;

            }
            else if (BytePerSample == 4)
            {
                var sv = BitConverter.ToInt32(_decodedData, seek);
                s = (float)sv / int.MaxValue;
                seek += 4;

            }

            return s;
        }

        public int ReadFloat(float[] data, int offset, int count)
        {
            int left = (_decodedData.Length - seek) / BytePerSample;
            int readcount = Math.Min(left, count);
            for (var i = 0; i < readcount; i++)
            {
                data[i] = decodeOne();
            }

            return readcount;

        }
    }
}
